﻿#include "DbsSqlite3.h"
#include "sqlite3.h"
#include <iostream>
#include <string>
#include <mutex>


#pragma comment(lib, "sqlite3.lib")

using namespace std;

struct DbsSqlite3::DbsSqlite3Impl
{
    sqlite3 * dbConn{nullptr};
    sqlite3_stmt* stmt{nullptr};
    std::string tableName{"apppacket"};
    int status{ SQLITE_DONE };
    std::mutex mtx;// 加锁
};

DbsSqlite3::DbsSqlite3()
    : Dbs()
{
    m_impl = new DbsSqlite3Impl;
}

DbsSqlite3::~DbsSqlite3()
{
    delete m_impl;
}


void DbsSqlite3::SetTableName(const std::string& name)
{
    m_impl->tableName = name;
}

bool DbsSqlite3::MakeTable()
{
    bool result = false;
    do
    {
        int32_t count = 0;
        /**/
        std::string sql = "select count(*) from sqlite_master where type = 'table' and name = '" + m_impl->tableName +"';";
        if ((count = GetCount(sql.c_str())) == -1)
            break;
        sql = "CREATE TABLE \""+ m_impl->tableName +"\" (\"relative\" TEXT NOT NULL,\"md5\" TEXT, \"packet\" INTEGER DEFAULT -1, PRIMARY KEY (\"relative\" ASC));";
        if (count == 0 && !Exec(sql.c_str()))
            break;

        sql = "select count(*) from sqlite_master where type = 'table' and name = 'appversion';";
        if ((count = GetCount(sql.c_str())) == -1)
            break;
        sql = "CREATE TABLE \"appversion\" (\"app\" TEXT NOT NULL, \"version\" TEXT, \"packet\" INTEGER DEFAULT -1, \"prepare\" INTEGER DEFAULT -1, PRIMARY KEY(\"app\" ASC) );";
        if (count == 0 && !Exec(sql.c_str()))
            break;
        result = true;
    } while (0);
    return result;
}


int DbsSqlite3::GetCount(const char* sql)
{
    bool res{ false };
    int count{ -1 };
    sqlite3_stmt* stmt = NULL;
    const char* pTail = NULL;
    int ret = sqlite3_prepare(m_impl->dbConn, sql, -1, &stmt, &pTail);
    if (SQLITE_OK == ret)
    {
        res = false;
        if (stmt)
        {
            ret = sqlite3_step(stmt);
            switch (ret)
            {
            case SQLITE_ROW:
            {
                count = sqlite3_column_int(stmt, 0);
                res = true;
            }
            break;
            case SQLITE_DONE:
            {
                res = true;
                count = 0;
            }
            break;
            default:
            {
                res = false;
            }
            break;
            }
        }
    }
    sqlite3_finalize(stmt);
    return count;
}

bool DbsSqlite3::Open()
{
    int nRes = sqlite3_open("F:\\Project\\voidv\\trunk\\src\\tool\\qtools\\AutoUpdate\\AutoUpdatePacket.db", &m_impl->dbConn);
    if (nRes != SQLITE_OK)
    {
        cout << "Open database fail: " << sqlite3_errmsg(m_impl->dbConn);
        return false;
    }
    return true;
}

void DbsSqlite3::Close()
{
    if (m_impl->dbConn)
    {
        sqlite3_close(m_impl->dbConn);
    }
}


bool DbsSqlite3::Inquiry(const char* sql)
{
    sqlite3_stmt* stmt = NULL;
    sqlite3_prepare(m_impl->dbConn, sql, -1, &stmt, NULL);

    sqlite3_step(stmt);

    sqlite3_finalize(stmt);
    return false;
}

bool DbsSqlite3::Exec(const char* sql)
{
    char* cErrMsg{ nullptr };
    int res = sqlite3_exec(m_impl->dbConn, sql, 0, 0, &cErrMsg);
    if (res != SQLITE_OK)
    {
        cout << "select fail: " << cErrMsg << endl;
        sqlite3_free(cErrMsg);
        return false;
    }
    return true;
}

int DbsSqlite3::SelectFileInfo(const std::string& relative, StFileInfo& info)
{
    std::string sql = "select relative, md5, packet from " + m_impl->tableName + " where relative='" + relative + "'";

    int count{ -1 };
    sqlite3_stmt* stmt = NULL;
    const char* pTail = NULL;
    int ret = sqlite3_prepare(m_impl->dbConn, sql.c_str(), -1, &stmt, &pTail);
    if (SQLITE_OK == ret)
    {
        bool res = false;
        if (stmt)
        {
            ret = sqlite3_step(stmt);
            switch (ret)
            {
            case SQLITE_ROW:
            {
                info.relative = (const char*)sqlite3_column_text(stmt, 0);
                info.md5 = (const char*)sqlite3_column_text(stmt, 1);
                info.packet = sqlite3_column_int64(stmt, 2);
                count = 1;
                res = true;
            }
            break;
            case SQLITE_DONE:
            {
                count = 0;
                res = true;
            }
            break;
            default:
            {
                res = false;
            }
            break;
            }
        }
    }
    sqlite3_finalize(stmt);
    return count;
}
bool DbsSqlite3::InsertFileInfo(const StFileInfo* info, int size)
{
    if (size <= 0 || !info)
    {
        return false;
    }
    bool res{ false };
    int index{ 0 };
    while (1)
    {
        std::string sql;
        for (; index < size; ++index)
        {
            std::string sqlt = "insert into " + m_impl->tableName + " (relative, md5, packet) values ('" 
                + info[index].relative + "','" 
                + info[index].md5 + "', "
                + std::to_string(info[index].packet) + "); ";
            sql += sqlt;
            if (0 != index && 0 == index % 1000)
            {
                break;
            }
        }
        if (!sql.empty())
        {
            if (!Exec(sql.c_str()))
                break;
        }
        if (index >= size)
        {
            res = true;
            break;
        }
    }
    return res;
}

bool DbsSqlite3::UpdateFileInfo(const StFileInfo * info, int size)
{
    if (size <= 0 || !info)
    {
        return false;
    }
    bool res{ false };
    int index{ 0 };
    while (1)
    {
        std::string sql;
        for (; index < size; ++index)
        {
            std::string sqlt = "update " + m_impl->tableName + " set relative = '" + info[index].relative 
                + "', md5 = '" + info[index].md5
                + "', packet = " + std::to_string(info[index].packet)
                + " where relative = '" + info[index].relative + "';";
            sql += sqlt;
            if (0 != index && 0 == index % 1000)
            {
                break;
            }
        }
        if (!sql.empty()) 
        {
            if (!Exec(sql.c_str()))
                break;
        }
        if (index >= size)
        {
            res = true;
            break;
        }
    }
    return res;
}

bool DbsSqlite3::SelectFileLatestPacket(int64_t& packet)
{
    std::string sql = "select packet from " + m_impl->tableName + " order by packet desc limit 0, 1;";
    sqlite3_stmt* stmt = NULL;
    const char* pTail = NULL;
    int ret = sqlite3_prepare(m_impl->dbConn, sql.c_str(), -1, &stmt, &pTail);
    if (SQLITE_OK == ret)
    {
        bool res = false;
        if (stmt)
        {
            ret = sqlite3_step(stmt);
            switch (ret)
            {
            case SQLITE_ROW:
            {
                packet = sqlite3_column_int64(stmt, 0);
                res = true;
            }
            break;
            case SQLITE_DONE:
            {
                packet = -1;
                res = true;
            }
            break;
            default:
            {
                res = false;
            }
            break;
            }
        }
    }
    sqlite3_finalize(stmt);
    return true;
}

int DbsSqlite3::SelectVersionInfo(const std::string& app, StVersionInfo& info)
{
    std::string sql = "select app, version, packet, prepare from appversion where app='" + app + "'";
    int count{ -1 };
    sqlite3_stmt* stmt = NULL;
    const char* pTail = NULL;
    int ret = sqlite3_prepare(m_impl->dbConn, sql.c_str(), -1, &stmt, &pTail);
    if (SQLITE_OK == ret)
    {
        bool res = false;
        if (stmt)
        {
            ret = sqlite3_step(stmt);
            switch (ret)
            {
            case SQLITE_ROW:
            {
                info.app = (const char*)sqlite3_column_text(stmt, 0);
                info.version = (const char*)sqlite3_column_text(stmt, 1);
                info.packet = sqlite3_column_int64(stmt, 2);
                info.prepare = sqlite3_column_int64(stmt, 3);
                count = 1;
                res = true;
            }
            break;
            case SQLITE_DONE:
            {
                count = 0;
                res = true;
            }
            break;
            default:
            {
                res = false;
            }
            break;
            }
        }
    }
    sqlite3_finalize(stmt);
    return count;
}


bool DbsSqlite3::InsertVersionInfo(const StVersionInfo& info)
{
    bool res{ false };
    do 
    {
        std::string sql = "insert into appversion (app, packet, prepare, version) values ('" 
            + info.app + "', " + std::to_string(info.packet) + ", " + std::to_string(info.prepare)
            + ", '" + info.version + "'); ";
        if (!Exec(sql.c_str()))
            break;
        res = true;
    } while (0);
    return res;
}
bool DbsSqlite3::UpdateVersionInfo(const StVersionInfo& info) 
{
    bool res{ false };
    int index{ 0 };
    do
    {
        std::string sql = "update appversion set app = '" + info.app 
            + "', packet = " + std::to_string(info.packet)
            + ", prepare = " + std::to_string(info.prepare)
            + ", version = '" + info.version 
            + "' where app = '" + info.app + "';";
        if (!Exec(sql.c_str()))
            break;
        res = true;
    } while (0);
    return res;
}

int64_t DbsSqlite3::GetColumnInt64(int col)
{
    return sqlite3_column_int64(m_impl->stmt, 1);
}
